/*
-----------------------------------------------------------------------------
This source file is part of Labor3D
(Labor 3D Graphics Engine)

Copyright (c) 2009-2020 Rock Mao
* creator : Rock Mao
* created : 2009-6-27   22:29
* filename: LaborD3D10HardwareBuffer.cpp
-----------------------------------------------------------------------------
*/
#include "LaborD3D10HardwareBuffer.h"
#include "LaborD3D10Helps.h"
NAMESPACE_LABOR3D_BEGIN
//---------------------------------------------------------------------
CLD3D10HardwareBuffer::CLD3D10HardwareBuffer(eLHardwareBufferType btype, size_t sizeBytes, EHB_Usage usage, 
											 CLD3D10Device & device, bool useSystemMemory, bool useShadowBuffer)
											 : CLHardwareBuffer(usage, useSystemMemory,  false /* TODO: useShadowBuffer*/),
											 mlpD3DBuffer(0),
											 mpTempStagingBuffer(0),
											 mUseTempStagingBuffer(false),
											 mBufferType(btype),
											 mDevice(device)
{
	mSizeInBytes = sizeBytes;
	mDesc.ByteWidth = static_cast<UINT>(sizeBytes);
	mDesc.CPUAccessFlags = CLD3D10Helps::_getAccessFlags(mUsage); 

	if (useSystemMemory)
	{
		mDesc.Usage = D3D10_USAGE_STAGING;
		//A D3D10_USAGE_STAGING Resource cannot be bound to any parts of the graphics pipeline, so therefore cannot have any BindFlags bits set.
		mDesc.BindFlags = 0;
		mDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ ;// A D3D10_USAGE_STAGING Resource must have at least one CPUAccessFlag bit set.

	}
	else
	{
		mDesc.Usage = CLD3D10Helps::_getUsage(mUsage);
		mDesc.BindFlags = CLD3D10Helps::_getBufferBindFlag( btype );

	}

	mDesc.MiscFlags = 0;
	if (!useSystemMemory && (usage | HBU_DYNAMIC))
	{
		// We want to be able to map this buffer
		mDesc.CPUAccessFlags |= D3D10_CPU_ACCESS_WRITE;
		mDesc.Usage = D3D10_USAGE_DYNAMIC;
	}

	// Note that in D3D10 you can only directly lock (map) a dynamic resource
	// directly for writing. You can only map for read / write staging resources,
	// which themselves cannot be used for input / output to the GPU. Thus
	// for any locks except write locks on dynamic resources, we have to use
	// temporary staging resources instead and use async copies.
	// We use the 'useSystemMemory' option to indicate a staging resource


	// TODO: we can explicitly initialise the buffer contents here if we like
	// not doing this since OGRE doesn't support this model yet
	HRESULT hr = device->CreateBuffer( &mDesc, 0, &mlpD3DBuffer );
	if (FAILED(hr) || mDevice.isError())
	{

	}

}

//---------------------------------------------------------------------
CLD3D10HardwareBuffer::~CLD3D10HardwareBuffer()
{

}

//---------------------------------------------------------------------
void* CLD3D10HardwareBuffer::lockImpl(size_t offset, 
									  size_t length, EHB_LockOptions options)
{
	if ( NULL == mlpD3DBuffer )
		return NULL;
	
	if (length > mSizeInBytes)
	{
		// need to realloc
		mDesc.ByteWidth = static_cast<UINT>(mSizeInBytes);
		HRESULT hr = mDevice->CreateBuffer( &mDesc, 0, &mlpD3DBuffer );
		if (FAILED(hr) || mDevice.isError())
		{

		}
	}

	// Staging (system memory) buffers or dynamic, write-only buffers 
	// are the only case where we can lock directly
	// We have no 'lock for writing' but discard + locking the entire
	// buffer essentially means the same thing, especially since it's
	// not possible to map part of a buffer in Dx10

	// map directly
	D3D10_MAP mapType;

	mapType = (D3D10_MAP)options;

	void* pRet;
	//mDevice.clearStoredErrorMessages();
	HRESULT hr = mlpD3DBuffer->Map(mapType, 0, &pRet);
	if (FAILED(hr) /*|| mDevice.isError()*/)
	{
	}

	// In Dx10 we can only map entire buffer, not subregions, 
	// but we can offset the pointer for compat
	if (offset)
	{
		pRet = static_cast<void*>(static_cast<char*>(pRet) + offset);
	}

	return pRet;


}
//---------------------------------------------------------------------
void CLD3D10HardwareBuffer::unlockImpl(void)
{

	//if (mUseTempStagingBuffer)
	//{
	//	mUseTempStagingBuffer = false;

	//	// ok, we locked the staging buffer
	//	mpTempStagingBuffer->unlock();

	//	// copy data if needed
	//	// this is async but driver should keep reference
	//	if (mStagingUploadNeeded)
	//		copyData(*mpTempStagingBuffer, 0, 0, mSizeInBytes, true);

	//	// delete
	//	// not that efficient, but we should not be locking often
	//	SafeDelete(mpTempStagingBuffer);
	//}
	//else
	//{
	// unmap
	if ( mlpD3DBuffer )
		mlpD3DBuffer->Unmap();
	//}
}
//---------------------------------------------------------------------
void CLD3D10HardwareBuffer::copyData(CLHardwareBuffer& srcBuffer, size_t srcOffset, 
									 size_t dstOffset, size_t length, bool discardWholeBuffer)
{
	// If we're copying same-size buffers in their entirety...
	if (srcOffset == 0 && dstOffset == 0 &&
		length == mSizeInBytes && mSizeInBytes == srcBuffer.getSizeInBytes())
	{
		// schedule hardware buffer copy
		mDevice->CopyResource(mlpD3DBuffer, static_cast<CLD3D10HardwareBuffer&>(srcBuffer).getD3DBuffer());
		if (mDevice.isError())
		{

		}
	}
	else
	{
		// copy subregion
		D3D10_BOX srcBox;
		srcBox.left = (UINT)srcOffset;
		srcBox.right = (UINT)srcOffset + length;
		srcBox.top = 0;
		srcBox.bottom = 1;
		srcBox.front = 0;
		srcBox.back = 1;

		mDevice->CopySubresourceRegion(mlpD3DBuffer, 0, (UINT)dstOffset, 0, 0, 
			static_cast<CLD3D10HardwareBuffer&>(srcBuffer).getD3DBuffer(), 0, &srcBox);

	}
}
//---------------------------------------------------------------------
void CLD3D10HardwareBuffer::readData(size_t offset, size_t length, 
									 void* pDest)
{
	// There is no functional interface in D3D, just do via manual 
	// lock, copy & unlock
	void* pSrc = this->lock(offset, length, eHBL_READ);
	memcpy(pDest, pSrc, length);
	this->unlock();

}
//---------------------------------------------------------------------
void CLD3D10HardwareBuffer::writeData(size_t offset, size_t length, 
									  const void* pSource,
									  bool discardWholeBuffer)
{
	// There is no functional interface in D3D, just do via manual 
	// lock, copy & unlock
	void* pDst = this->lock(offset, length, 
		discardWholeBuffer ? eHBL_WRITE_DISCARD : eHBL_READ_WRITE);
	memcpy(pDst, pSource, length);
	this->unlock();
}


NAMESPACE_LABOR3D_END